﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "CLQueue.h"
#include "CLMemory.h"
#include "CLKernel.h"

namespace UX_STL
{
namespace CLShell
{

/*
=================================================
	конструктор
=================================================
*/
	CLQueue::CLQueue(): _pID(null)
	{
	}
		
/*
=================================================
	деструктор
=================================================
*/
	CLQueue::~CLQueue()
	{
	}
	
/*
=================================================
	создать очередь
=================================================
*/
	bool CLQueue::Create(cl_queue_property::type eFlags)
	{
		Destroy();

		cl_int	i_cl_err = 0;
		CL_CALL_R( _pID = clCreateCommandQueue( CLDEVICE()->Context(), CLDEVICE()->DeviceId(), eFlags, &i_cl_err ) );
		return true;
	}

/*
=================================================
	удаляет очередь
=================================================
*/
	void CLQueue::Destroy()
	{
		_ReleaseRef();
		_pID = null;
	}
	
/*
=================================================
	ждет отправки всех комманд в девайс
=================================================
*/
	bool CLQueue::Flush() const
	{
		CL_CALL_R( cl_int  i_cl_err = clFlush( _pID ) );
		return true;
	}
	
/*
=================================================
	ждет выполнения всех комманд
=================================================
*/
	bool CLQueue::Finish() const
	{
		CL_CALL_R( cl_int  i_cl_err = clFinish( _pID ) );
		return true;
	}
	
/*
=================================================
	добавляет в очередь команду, которая
	гарантирует, что все предыдущие команды выполнены
=================================================
*/
	bool CLQueue::EnqueueBarrier() const
	{
		CL_CALL_R( cl_int  i_cl_err = clEnqueueBarrier( _pID ) );
		return true;
	}
	
/*
=================================================
	ожидать срабатывания событий до выполнения
	следующих команд в очереди
=================================================
*/
	bool CLQueue::EnqueueWait(const clevent_uni_array &aEvents) const
	{
		CL_CALL_R( cl_int  i_cl_err = clEnqueueWaitForEvents( _pID, (cl_uint) aEvents.Count(), aEvents.ptr() ) );
		return true;
	}
		
/*
=================================================
	создает маркер
=================================================
*/
	bool CLQueue::EnqueueMarker(CLEvent *&pEvent) const
	{
		cl_int		i_cl_err = 0;
		cl_event	ev		 = null;

		CL_CALL_R( i_cl_err = clEnqueueMarker( _pID, &ev ) );
		
		if ( ev != null ) {
			pEvent = new CLEvent( ev );
		}
		return true;
	}
	
/*
=================================================
	вызывает C/C++ функцию
=================================================
*
	bool CLQueue::EnqueueNativeKernel(void (*pUserFunc)(void *), void *pArgs, usize uArgsSize,
							const mem_obj_array_t &aMemList, const clevent_uni_array &aEvents, CLEvent **pEvent) const
	{
		CHECK_ERR( CLDEVICE()->IsNativeKernelSupported() );
		CHECK_ERR( pUserFunc != null );

		cl_int				i_cl_err = 0;
		cl_event			ev		 = null;

		array< cl_mem >		a_mem;
		pvoid_array_t		a_mem_loc;
		array< cl_event >	a_events;

		for (usize i = 0; i < aEvents.Count(); ++i)
			a_events.PushBack( aEvents[i]->Id() );

		for (usize i = 0; i < aMemList.Count(); ++i)
		{
			a_mem.PushBack( aMemList[i].pMem->Id() );
			a_mem_loc.PushBack( aMemList[i].pPtr );
		}

		CL_CALL_R( i_cl_err = clEnqueueNativeKernel( _pID, pUserFunc, pArgs, uArgsSize, aMemList.Count(),
													 a_mem.ptr(), (const void **)a_mem_loc.ptr(), a_events.Count(),
													 a_events.ptr(), pEvent != null ? &ev : null ) );
		if ( ev != null )
		{
			*pEvent = new CLEvent( ev );
		}
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CLQueue::EnqueueTask(CLKernel *pKernel, const clevent_uni_array &aEvents, CLEvent **pEvent) const
	{
		CHECK_ERR( pKernel != null );

		cl_int		i_cl_err = 0;
		cl_event	ev		 = null;

		CL_CALL_R( i_cl_err = clEnqueueTask( Id(), pKernel->Id(), (cl_uint) aEvents.Count(), aEvents.ptr(),
											 pEvent != null ? &ev : null ) );

		if ( ev != null ) {
			*pEvent = new CLEvent( ev );
		}
		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CLQueue::EnqueueNDRange(CLKernel *pKernel, const svec3 &vGlobalWorkOffsets,
								 const svec3 &vGlobalWorkSizes, const svec3 &vLocalWorkSizes,
								 const clevent_uni_array &aEvents, CLEvent **pEvent) const
	{
		CHECK_ERR( pKernel != null );
		
		cl_int		i_cl_err	= 0;
		cl_event	ev			= null;
		cl_uint		u_work_dim	= (vGlobalWorkSizes[0] != 0) +
								  (vGlobalWorkSizes[1] != 0) +
								  (vGlobalWorkSizes[2] != 0);

		CL_CALL_R( i_cl_err = clEnqueueNDRangeKernel( Id(), pKernel->Id(),
													  u_work_dim,
													  vGlobalWorkOffsets.Sum() == 0 ? null : vGlobalWorkOffsets.ptr(),
													  vGlobalWorkSizes.ptr(),
													  vLocalWorkSizes.Sum() == 0 ? null : vLocalWorkSizes.ptr(),
													  (cl_uint) aEvents.Count(),
													  aEvents.raw_ptr(),
													  pEvent != null ? &ev : null ) );
		if ( ev != null ) {
			*pEvent = new CLEvent( ev );
		}
		return true;
	}

/*
=================================================
	
=================================================
*/
	bool CLQueue::EnqueueAcquireGLObjects(const clmem_uni_array &aMemList, const clevent_uni_array &aEvents, CLEvent **pEvent) const
	{
		cl_int		i_cl_err = 0;
		cl_event	ev		 = null;

		CL_CALL_R( i_cl_err = clEnqueueAcquireGLObjects( _pID, (cl_uint) aMemList.Count(), aMemList.ptr(),
														(cl_uint) aEvents.Count(), aEvents.raw_ptr(),
														pEvent != null ? &ev : null ) );

		if ( ev != null ) {
			*pEvent = new CLEvent( ev );
		}
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CLQueue::EnqueueReleaseGLObjects(const clmem_uni_array &aMemList, const clevent_uni_array &aEvents, CLEvent **pEvent) const
	{
		cl_int		i_cl_err = 0;
		cl_event	ev		 = null;

		CL_CALL_R( i_cl_err = clEnqueueReleaseGLObjects( _pID, (cl_uint) aMemList.Count(), aMemList.ptr(),
														(cl_uint) aEvents.Count(), aEvents.raw_ptr(),
														pEvent != null ? &ev : null ) );

		if ( ev != null ) {
			*pEvent = new CLEvent( ev );
		}
		return true;
	}

	__UX_CL_RC_IMPL( CLQueue, _pID, CommandQueue, QUEUE )

//-------------------------------------------------------------------

}	// CLShell
}	// UX_STL